<?php

namespace addons\booking\model;

use think\Model;
use addons\booking\model\OrderCalendar;
use addons\booking\model\Lodgers;
use think\Config;
use addons\booking\library\order\ToPlanOrder;
use addons\booking\library\order\OrderHouse;
use addons\booking\library\order\OrderGoods;
use Yansongda\Pay\Exceptions\GatewayException;
use addons\epay\library\Service;

class Order extends Model
{

    // 表名
    protected $name = 'booking_order';

    // 自动写入时间戳字段
    protected $autoWriteTimestamp = 'int';

    // 定义时间戳字段名
    protected $createTime = 'createtime';
    protected $updateTime = 'updatetime';
    protected $deleteTime = false;

    // 追加属性
    protected $append = [
        'type_text',
        'paytime_text',
        'checkin_time_text',
        'leave_time_text',
        'status_text'
    ];


    public function getTypeList()
    {
        return ['0' => __('Type 0'), '1' => __('Type 1'), '2' => __('Type 2')];
    }

    public function getStatusList()
    {
        return [
            'created'     => __('Status created'),
            'paid'        => __('Status paid'),
            'expired'     => __('Status expired'),
            'canceled'    => __('Status canceled'),
            'shipped'     => __('Status shipped'),
            'refunding'   => __('Status refunding'),
            'evaluate'    => __('Status evaluate'),
            'toshopay'    => __('Status toshopay'),
            'checkined'   => __('Status checkined'),
            'checkouting' => __('Status checkouting'),
            'finished'    => __('Status finished')
        ];
    }


    public function getTypeTextAttr($value, $data)
    {
        $value = $value ? $value : (isset($data['type']) ? $data['type'] : '');
        $list = $this->getTypeList();
        return isset($list[$value]) ? $list[$value] : '';
    }


    public function getPaytimeTextAttr($value, $data)
    {
        $value = $value ? $value : (isset($data['paytime']) ? $data['paytime'] : '');
        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
    }


    public function getCheckinTimeTextAttr($value, $data)
    {
        $value = $value ? $value : (isset($data['checkin_time']) ? $data['checkin_time'] : '');
        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
    }


    public function getLeaveTimeTextAttr($value, $data)
    {
        $value = $value ? $value : (isset($data['leave_time']) ? $data['leave_time'] : '');
        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
    }


    public function getStatusTextAttr($value, $data)
    {
        $value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
        $list = $this->getStatusList();
        return isset($list[$value]) ? $list[$value] : '';
    }


    protected function setCheckinTimeAttr($value)
    {
        return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
    }

    protected function setLeaveTimeAttr($value)
    {
        return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
    }

    protected static function init()
    {

        self::afterUpdate(function ($row) {
            $changeData = $row->getChangedData();
            if (isset($changeData['status'])) {
                //取消订单，退款，修改状态，是否售后
                //已预订房间状态修改
                if ($changeData['status'] == 'canceled') {
                    OrderCalendar::editCalendar($row->orderid);
                }
                //退房结束，评价晚，给积分
                if ($changeData['status'] == 'finished') {
                    $config = get_addon_config('booking');
                    if (isset($config['money_score']) && $config['money_score'] > 0) {
                        $money = bcsub($row['payamount'], $row['outamount'], 2);
                        if ($money > 0) {
                            $score = bcmul($money, $config['money_score']);
                            \app\common\model\User::score($score, $row['user_id'], '完成入住订单赠送积分');
                        }
                    }
                }
            }
        });
    }

    public function setParamsAttr($value)
    {
        if (is_array($value)) {
            return json_encode($value);
        }
        return $value;
    }

    /**
     * 创建订单，返回订单id，去支付
     *
     * @param string $type
     * @param array  $param
     * @return void
     */
    public static function add($type, $param)
    {
        //订单信息
        $orderid = date("Ymdhis") . sprintf("%08d", $param['user_id']) . mt_rand(1000, 9999);
        $orderInfo['orderid'] = $orderid;
        $orderInfo['type'] = $type;
        $orderInfo['user_id'] = $param['user_id'];
        $orderInfo['amount'] = 0;
        $orderInfo['ip'] = request()->ip();
        $orderInfo['useragent'] = substr(request()->server('HTTP_USER_AGENT'), 0, 255);
        $orderInfo['memo'] = $param['memo'];
        $orderInfo['params'] = [];
        $obj = null;
        switch ($type) {
            case 0: //房间订单
                $orderInfo['nums'] = $param['nums'];
                $obj = new ToPlanOrder(new OrderHouse());
                break;
            case 1: //商品订单
                $orderInfo['params']['house_number'] = $param['house_number'];
                $obj = new ToPlanOrder(new OrderGoods());
                break;
        }
        return $obj->doing($orderInfo, $param);
    }

    /**
     * 订单支付
     *
     * @param int    $id 订单主键ID
     * @param int    $user_id
     * @param string $paytype
     * @param string $method
     * @param string $openid
     * @param string $notifyurl
     * @param string $returnurl
     * @return \addons\epay\library\Collection|\addons\epay\library\RedirectResponse|\addons\epay\library\Response|null
     */
    public static function pay($id, $user_id, $paytype, $method, $openid, $notifyurl, $returnurl)
    {
        $request = \think\Request::instance();
        $order = (new self)->where('id', $id)->where('user_id', $user_id)->find();
        if (!$order) {
            throw new \Exception('订单不存在！');
        }
        if ($order['status'] !== 'created') {
            throw new \Exception('订单已支付完成或已失效！');
        }
        //支付金额为0，无需支付
        if ($order->amount == 0) {
            throw new \Exception('无需支付！');
        }
        switch ($order['type']) {
            case 0: //房间 检验是否在
                $house = $order->source;
                if (empty($house)) {
                    throw new \Exception('房间不存在或已下架！');
                }
                if ($house->status != 'up') {
                    throw new \Exception('房间已下架！');
                }
                break;
            case 1:
                //校验商品的库存，状态
                $goods = $order->source()->with(['Goods'])->select();
                foreach ($goods as $item) {
                    if (empty($item['goods'])) {
                        throw new \Exception('存在失效商品！');
                    }
                    if (!isset($item['goods']['status']) || $item['goods']['status'] != 'up') {
                        throw new \Exception('存在下架商品！');
                    }
                    if ($item['nums'] > $item['goods']['stocks']) {
                        throw new \Exception('存在库存不足商品！');
                    }
                }
                break;
            default:
                throw new \Exception('订单类型不存在');
        }
        $orderid = $order->orderid;
        //支付方式变更
        if (($order['method'] && $order['paytype'] == $paytype && $order['method'] != $method)) {
            $orderid = date("Ymdhis") . sprintf("%08d", $user_id) . mt_rand(1000, 9999);
            //更新订单明细
            $sources = [];
            if ($order['type'] == 1) {
                $sources = $order->order_goods;
            } else {
                $sources = $order->order_calendar;
            }
            foreach ($sources as $item) {
                $item->orderid = $orderid;
                $item->save();
            }
            $subscribeLog = $order->subscribe_log;
            foreach ($subscribeLog as $item) {
                $item->orderid = $orderid;
                $item->save();
            }
            $order->save(['orderid' => $orderid]);
        }

        //更新支付类型和方法
        $order->save(['paytype' => $paytype, 'method' => $method]);

        $response = null;
        $epay = get_addon_info('epay');

        if ($epay && $epay['state']) {

            $notifyurl = $notifyurl ? $notifyurl : $request->root(true) . '/addons/booking/order/epay/type/notify/paytype/' . $paytype;
            $returnurl = $returnurl ? $returnurl : $request->root(true) . '/addons/booking/order/epay/type/return/paytype/' . $paytype . '/order_id/' . $orderid;

            //保证取出的金额一致，不一致将导致订单重复错误
            $amount = sprintf("%.2f", $order->amount);

            $params = [
                'amount'    => $amount,
                'orderid'   => $orderid,
                'type'      => $paytype,
                'title'     => "支付{$amount}元",
                'notifyurl' => $notifyurl,
                'returnurl' => $returnurl,
                'method'    => $method,
                'openid'    => $openid
            ];


            try {
                $response = Service::submitOrder($params);
            } catch (GatewayException $e) {
                throw new \Exception(config('app_debug') ? $e->getMessage() : "支付失败，请稍后重试");
            }
        } else {
            throw new \Exception("请在后台安装配置微信支付宝整合插件");
        }
        return $response;
    }

    /**
     * 订单结算
     *
     * @param string $orderid
     * @param float  $payamount
     * @return bool
     */
    public static function settle($orderid, $payamount)
    {
        //更新订单状态
        $order = self::where('orderid', $orderid)->where('status', 'created')->find();
        if (!$order) {
            return false;
        }
        if ($payamount != $order->amount) {
            \think\Log::write("[booking][pay][{$orderid}][订单支付金额不一致]");
            return false;
        }
        $order->status = 'paid';
        $order->payamount = $payamount;
        $order->paytime = time();
        $order->save();

        //支付完成后,商品销量+1
        if ($order['type'] == 1) {
            \addons\booking\model\OrderGoods::setGoodsSalesInc($orderid);
        }
    }


    /**
     * 订单列表
     *
     * @return void
     */
    public static function tableList($param)
    {
        $pageNum = 15;
        if (isset($param['num']) && !empty($param['num'])) {
            $pageNum = $param['num'];
        }

        $type = isset($param['type']) ? (int)$param['type'] : 0;
        $withArr = [];
        $field = 'id,type,orderid,source_id,user_id,user_coupon_id,amount,coupon_amount,status';
        //0=房间，1=商品，2=其他
        switch ($type) {
            case 1:
                $withArr['orderGoods'] = function ($query) {
                    $query->field('*');
                };
                $field .= ',nums,delivery_time';
                break;
            default:
                $withArr['House'] = function ($query) {
                    $query->field('id,name,images,checkin_time,checkout_time');
                };
                $withArr['Subscribe'] = function ($query) {
                    $query->field('id,orderid');
                };
                $field .= ',checkin_time,leave_time';
        }

        return self::field($field)
            ->with($withArr)
            ->where(function ($query) use ($param, $type) {

                $query->where('type', $type);

                if (isset($param['status']) && !empty($param['status'])) {
                    $query->where('status', $param['status']);
                }

                if (isset($param['user_id']) && !empty($param['user_id'])) {
                    $query->where('user_id', $param['user_id']);
                }
            })->order('createtime desc')->paginate($pageNum);
    }


    /**
     * 订单详情
     *
     * @return void
     */
    public static function getDetail($id, $user_id)
    {

        $order = self::field('ip,coupon_id,payamount,useragent,method,paytype', true)->where('id', $id)->where('user_id', $user_id)->find();
        if (!$order) {
            return null;
        }
        //超过设置订单状态为过期
        if ($order['status'] == 'created' && time() - $order->createtime > Config::get('booking.order_timeout')) {
            $order->save(['status' => 'expired']);
            //已预订房间状态修改
            OrderCalendar::editCalendar($order->orderid);
        }
        //0=房间，1=商品，2=其他
        if ($order['type'] == 0) {

            $lodgerList = Lodgers::field('id,name,mobile')->where('id', 'IN', $order['lodgers_ids'])->select();

            foreach ($lodgerList as $item) {
                $item->mobile = substr_replace($item->mobile, '****', 3, 4);
            }
            $order->lodgers = $lodgerList;

            $order->source_data = $order->source()->with([
                'Type' => function ($query) {
                    $query->field('id,name');
                }
            ])->field('id,name,images,type_config_id,area,livenums')->find();

            $order->hidden(['params']);
        } elseif ($order['type'] == 1) {
            $order->source_data = $order->source()->field('*')->select();
            $hide = explode(',', 'actual_checkin_time,actual_checkout_time,checkin_time,checkin_time_text,leave_time,leave_time_text,lodgers_ids,checkin_time_text');
            $order->hidden($hide);
        }

        return $order;
    }


    /**
     * 资源关联
     *
     * @return \think\model\relation\BelongsTo|\think\model\relation\HasMany
     */
    public function source()
    {
        $type = $this->getData('type');
        //0=房间，1=商品，2=其他
        switch ($type) {
            case 0:
                return $this->belongsTo('House', 'source_id');
                break;
            case 1:
                return $this->hasMany('OrderGoods', 'orderid', 'orderid');
                break;
        }
    }

    public function orderGoods()
    {
        return $this->hasMany('OrderGoods', 'orderid', 'orderid');
    }

    public function orderCalendar()
    {
        return $this->hasMany('OrderCalendar', 'orderid', 'orderid');
    }

    public function subscribeLog()
    {
        return $this->hasMany('SubscribeLog', 'orderid', 'orderid');
    }

    public function House()
    {
        return $this->belongsTo('House', 'source_id', 'id', [], 'LEFT');
    }

    public function Subscribe()
    {
        return $this->belongsTo('SubscribeLog', 'orderid', 'orderid', [], 'LEFT');
    }
}
